package com.atguigu1.util;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import com.atguigu3.bean.Customer;
import com.atguigu3.bean.Order;
import com.mysql.jdbc.Connection;
import com.mysql.jdbc.Field;
import com.mysql.jdbc.Statement;
/**
 * 小结：
 * 面向接口编程的思想：
		ORM编程思想(object relational mapping)
		一个数据表对应一个java类
		表中的一条记录对应java类的以个对象
		表中的一个字段对应java类的一个属性
   两种技术：
        JDBC结果集的元数据：ResultSetMetaData
           ---获取列数：getColumnCount()
           ---获取列的别名：getColumnLabel()
        通过反射，创建指定类的对象，获取指定的属性并赋值。
 * @author 石文学
 *
 */
public class JDBCUtils {
	/**
	 * 获取数据库的连接
	 * @throws IOException 
	 */
	public static Connection getConnection() throws Exception
	{
		//1.读取配置文件
		InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
		Properties pros = new Properties();
		pros.load(is);
		
		String user = pros.getProperty("user");
		String password = pros.getProperty("password");
		String url = pros.getProperty("url");
		String driverClass = pros.getProperty("driverClass");
		
		//2.加载驱动
		Class.forName(driverClass);
		
		//3.获取连接
		Connection conn = (Connection) DriverManager.getConnection(url,user,password);
		return conn;
	}
	
	/**
	 * 关闭资源操作
	 */
	public static void closeResource(Connection conn,PreparedStatement ps)
	{
		if(ps!=null)
		{
			try {
				ps.cancel();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		if(conn!=null)
		{	
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * 关闭资源操作
	 * @param sql
	 * @param args
	 */
	public static void closeResource(java.sql.Connection conn,PreparedStatement ps,ResultSet resultSet)
	{
		if(ps!=null)
		{
			try {
				ps.cancel();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		if(conn!=null)
		{	
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		if(resultSet!=null)
		{
			try {
				resultSet.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * 通用增删改操作
	 * @param sql
	 * @param args
	 */
	public static void update(String sql,Object ...args)//sql中占位符的个数与可变形参的长度一样
	{
		Connection conn = null;
		PreparedStatement ps = null;
		try {
			//1.获取数据库的连接
			conn = JDBCUtils.getConnection();
			//2.预编译sql语句，返回perparedstatement的实例
			ps = (PreparedStatement) conn.prepareStatement(sql);
			//3.填充占位符
			for(int i=0;i<args.length;i++)
			{
				ps.setObject(i+1, args[i]);//小心参数声明错误
			}
			//4.执行
			ps.execute();
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			//5.关闭资源
			JDBCUtils.closeResource(conn, ps);
		}
	}
	
	/**
	 *针对于customers表的通用查询操作
	 */
	public static Customer CustomerCommonForQuery(String sql,Object ...args)
	{
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			conn = JDBCUtils.getConnection();
			ps = conn.prepareStatement(sql);
			for(int i=0;i<args.length;i++)
			{
				ps.setObject(i+1, args[i]);
			}
			
			rs = ps.executeQuery();
			//获取结果集的元数据，ResultSetMetadata
			ResultSetMetaData remd = rs.getMetaData();
			//通过ResultSetMetadata获得结果集中的列数
			int columncount = remd.getColumnCount();
			if(rs.next())
			{
				//方式一：
//			Class<Customer> clazz = (Class<Customer>) Class.forName("Customer");
//			Customer cust = clazz.newInstance();
				//方式二：
				Customer cust = new Customer();
				for(int i=0;i<columncount;i++)
				{
					//获取列值
					Object columValue = rs.getObject(i+1);
					
					//通过ResultSetMetadata
					//获取每个列的列名:getCatalogName()不推荐使用
					//获取每个列的别名:getColumnLabel()
					//String columName = remd.getCatalogName(i+1);
					String columnLabel = remd.getColumnLabel(i+1);
					
					//使用反射给每一个属性赋值：给cust对象指定的columnName属性，赋值为columValue:通过反射
					//java.lang.reflect.Field field = clazz.getDeclaredField(columName);
					java.lang.reflect.Field field = Customer.class.getDeclaredField(columnLabel);
					field.setAccessible(true);
					field.set(cust, columValue);
				}
				return cust;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			JDBCUtils.closeResource(conn, ps, rs);
		}
		return null;
	}
	
	/**
	 * 针对于order表通用的查询操作
	 * 针对于表的字段名与类的属性名不相同的情况：
	 * 1.必须声明sql时，使用类的属性名来命名字段的别名
	 * 2.使用ResultSetMetaData时，需要使用getColumnLabel()来替换getColumnName()
	 * 来获取列的别名。
	 * 说明，如果sql中没有给字段其别名，getColumnLabel()获取的就是列名。
	 * @throws Exception 
	 */
	public static Order OrderCommonForQuery(String sql,Object ...args)
	{
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			conn = JDBCUtils.getConnection();
			ps = conn.prepareStatement(sql);
			for(int i=0;i<args.length;i++)
			{
				ps.setObject(i+1, args[i]);
			}
			//获取结果集
			rs = ps.executeQuery();
			//获取结果集的元数据，ResultSetMetadata
			ResultSetMetaData remd = ps.getMetaData();
			//通过ResultSetMetadata获得结果集中的列数
			int columncount = remd.getColumnCount();
			if(rs.next())
			{
				Order order = new Order();
				for(int i=0;i<columncount;i++)
				{
					//获取列值
					Object columValue = rs.getObject(i+1);
					//通过ResultSetMetadata
					//获取每个列的列名:getCatalogName()不推荐使用
					//获取每个列的别名:getColumnLabel()
					//String columName = remd.getCatalogName(i+1);
					String columnLabel = remd.getColumnLabel(i+1);
					//使用反射给每一个属性赋值：给cust对象指定的columnName属性，赋值为columValue:通过反射
					java.lang.reflect.Field field = Order.class.getDeclaredField(columnLabel);					field.setAccessible(true);
					field.set(order, columValue);
				}
				return order;
			}
		}catch (Exception e) {
			e.printStackTrace();
		}finally {
			JDBCUtils.closeResource(conn, ps, rs);
		}
		return null;
	}
	
	/**
	 * 针对于不同表的通用查询操作,返回表中的一条记录
	 * @throws Exception 
	 */
	public static <T> T getInstance(Class<T> clazz,String sql,Object ...args)
	{
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			conn = JDBCUtils.getConnection();
			ps = conn.prepareStatement(sql);
			for(int i=0;i<args.length;i++)
			{
				ps.setObject(i+1, args[i]);
			}
			rs = ps.executeQuery();
			ResultSetMetaData remd = ps.getMetaData();
			int columnCount = remd.getColumnCount();
			if(rs.next())
			{
				T t = clazz.newInstance();
				for(int i=0;i<columnCount;i++)
				{
					Object columValue = rs.getObject(i+1);
					String columnLabel = remd.getColumnLabel(i+1);
					//使用反射给每一个属性赋值：给cust对象指定的columnName属性，赋值为columValue:通过反射
					java.lang.reflect.Field field = clazz.getDeclaredField(columnLabel);
					field.setAccessible(true);
					field.set(t, columValue);
				}
				return t;
			}
		}catch (Exception e) {
			e.printStackTrace();
		}finally {
			JDBCUtils.closeResource(conn, ps, rs);
		}
		return null;
	}
	
	
	/**
	 * 针对于不同表的通用查询操作,返回表中的多条记录
	 * @throws Exception 
	 */
	public static <T> List<T> testGetForList(Class<T> clazz,String sql,Object ...args)
	{
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			conn = JDBCUtils.getConnection();
			ps = conn.prepareStatement(sql);
			for(int i=0;i<args.length;i++)
			{
				ps.setObject(i+1, args[i]);
			}
			rs = ps.executeQuery();
			ResultSetMetaData remd = ps.getMetaData();
			int columnCount = remd.getColumnCount();
			//使用集合存储多个对象
			ArrayList<T> list = new ArrayList<>();
			while(rs.next())
			{
				T t = clazz.newInstance();
				for(int i=0;i<columnCount;i++)
				{
					Object columValue = rs.getObject(i+1);
					String columnLabel = remd.getColumnLabel(i+1);
					//使用反射给每一个属性赋值：给cust对象指定的columnName属性，赋值为columValue:通过反射
					java.lang.reflect.Field field = clazz.getDeclaredField(columnLabel);
					field.setAccessible(true);
					field.set(t, columValue);
				}
				list.add(t);
			}
			
			return list;
			
		}catch (Exception e) {
			e.printStackTrace();
		}finally {
			JDBCUtils.closeResource(conn, ps, rs);
		}
		return null;
	}
}
